package com.zywl.api;

import com.zywl.core.beans.AlipayFlow;
import com.zywl.core.beans.Association;
import com.zywl.core.beans.Dictionary;
import com.zywl.core.beans.PayRecord;
import com.zywl.service.AlipayFlowService;
import com.zywl.service.AssociationService;
import com.zywl.service.DictionaryService;
import com.zywl.service.PayRecordService;
import com.zywl.utils.models.JsonResult;
import com.zywl.utils.utils.Constants;
import com.zywl.utils.utils.DateUtil;
import com.zywl.utils.utils.FileUtil;
import com.zywl.utils.utils.WxPayUtil;
import com.zywl.web.controller.base.BaseController;
import com.zywl.web.controller.form.WxPrePayAmountForm;
import com.zywl.web.controller.form.WxPrePayForm;
import com.zywl.web.controller.form.WxPrePayPayerForm;
import net.sf.json.JSONObject;
import okhttp3.HttpUrl;
import org.apache.commons.lang.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyFactory;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;

@RestController
@RequestMapping("/api/wxpay/")
public class WxPayApi extends BaseController {
    //请求网关
    private static final String url_prex = "https://api.mch.weixin.qq.com/";
    //编码
    private static final String charset = "UTF-8";

    @Autowired
    private AlipayFlowService alipayFlowService;
    @Autowired
    private PayRecordService payRecordService;
    @Autowired
    private AssociationService associationService;
    @Autowired
    private DictionaryService dictionaryService;

    /**
     * 支付生成
     * @param assId
     * @param openId
     * @param prdId
     * @return
     */
    @ResponseBody
    @RequestMapping("wxpay")
    public JsonResult wxpay(HttpServletRequest request,Long assId, String openId, Long prdId){
        JsonResult jr = new JsonResult();
        Date date = new Date();
        try {
            if(prdId == null){
                return jr.error("请选择需要支付的订单");
            }
            if(assId == null){
                return jr.error("请输入协会id");
            }
            Association association = associationService.getById(assId);
            if(association == null){
                return jr.error("该协会不存在");
            }
            //根据订单id查询订单信息
            PayRecord payRecord = payRecordService.getById(prdId);
            if(payRecord == null){
                return jr.error("需要支付的订单不存在");
            }
            BigDecimal bigDecimal = new BigDecimal(payRecord.getPrdMoney());
            BigDecimal bd = new BigDecimal(100);
            int fee =  bigDecimal.multiply(bd).intValue();

            List<Dictionary> dictionaryList = dictionaryService.getDictionaryByType("zfje");
            if(dictionaryList.size() > 0 && "1".equals(dictionaryList.get(0).getDisCode())){
                fee =  1;
            }

            //生成流水号
            String afwFlowNo = "setmeal_"+ DateUtil.getServerTime("yyyyMMddHHmmss")+"PA"+prdId;

            AlipayFlow alipayFlow = new AlipayFlow();
            alipayFlow.setAfwAssId(assId);
            alipayFlow.setAfwFlowNo(afwFlowNo);
            alipayFlow.setAfwAtaIds(prdId+"");
            alipayFlow.setAfwPrice(new BigDecimal(payRecord.getPrdMoney()));
            alipayFlow.setAfwStt("0");
            alipayFlow.setAfwCreateTime(date);
            alipayFlow.setAfwModifyTime(date);
            alipayFlowService.save(alipayFlow);

            String wxPayRequestJsonStr = JsonStr(openId,fee,afwFlowNo,"套餐支付",
                    association.getAssWxAppId(),association.getAssWxMchId());
            logger.info(wxPayRequestJsonStr);
            if(StringUtils.isEmpty(association.getAssWxApiclientKeyPath())){
//            if(Thread.currentThread().getContextClassLoader().getResource("zjyd_apiclient_key.pem") == null){
                return jr.error("微信支付证书错误，请联系管理员");
            }
            StringBuffer sb=new StringBuffer();
//            sb.append("xhgl");
            sb.append(association.getAssWxApiclientKeyPath());
            String path = FileUtil.getFileRealPathForServer(request, sb.toString());
            System.out.println(path);
//            String path = Thread.currentThread().getContextClassLoader().getResource("zjyd_apiclient_key.pem").getPath();
//            String path = "/mnt/storage/tomcat/webapps/ROOT/WEB-INF/classes/apiclient_key.pem";
            //第一步获取prepay_id
            String prepayId = V3PayGet("v3/pay/transactions/jsapi", wxPayRequestJsonStr, association.getAssWxMchId(),
                    association.getAssWxSerialId(), path);
            //第二步获取调起支付的参数
            JSONObject object = JSONObject.fromObject(WxTuneUp(prepayId, association.getAssWxAppId(),path));
            jr.success("唤起支付页面",object);
        }catch (Exception e){
            logger.error("微信支付下单错误",e);
            jr.error("微信支付错误");
        }
        return jr;
    }

    public static String JsonStr(String openid,int fee,String outTradeNo,String boby,String wxAppId,String wxMchId) throws Exception{
        WxPrePayForm wxPrePayForm= new WxPrePayForm();
        wxPrePayForm.setAppid(wxAppId);
        wxPrePayForm.setMchid(wxMchId);
        wxPrePayForm.setDescription(boby);
        wxPrePayForm.setOut_trade_no(outTradeNo);
        wxPrePayForm.setNotify_url(Constants.NOTIFY_URL);

        WxPrePayAmountForm wxPrePayAmountForm = new WxPrePayAmountForm();
        wxPrePayAmountForm.setTotal(fee);
        wxPrePayAmountForm.setCurrency("CNY");
        wxPrePayForm.setAmount(wxPrePayAmountForm);

        WxPrePayPayerForm wxPrePayPayerForm = new WxPrePayPayerForm();
        wxPrePayPayerForm.setOpenid(openid);
        wxPrePayForm.setPayer(wxPrePayPayerForm);

        ObjectMapper mapper = new ObjectMapper();
        String wxPayRequestJsonStr = mapper.writeValueAsString(wxPrePayForm);
        return  wxPayRequestJsonStr;
    }

    /**
     * 微信支付下单
     *
     * @param url                请求地址（只需传入域名之后的路由地址）
     * @param jsonStr            请求体 json字符串 此参数与微信官方文档一致
     * @param mercId             商户ID
     * @param serial_no          证书序列号
     * @param privateKeyFilePath 私钥的路径
     * @return 订单支付的参数
     * @throws Exception
     */
    public static String V3PayGet(String url, String jsonStr, String mercId, String serial_no, String privateKeyFilePath) throws Exception {
        String body = "";
        //创建httpclient对象
        CloseableHttpClient client = HttpClients.createDefault();
        //创建post方式请求对象
        HttpPost httpPost = new HttpPost(url_prex + url);
        //装填参数
        StringEntity s = new StringEntity(jsonStr, charset);
        s.setContentEncoding(new BasicHeader(HTTP.CONTENT_TYPE,
                "application/json"));
        //设置参数到请求对象中
        httpPost.setEntity(s);
        String post = getToken("POST", HttpUrl.parse(url_prex + url), mercId, serial_no, privateKeyFilePath, jsonStr);
        //设置header信息
        //指定报文头【Content-type】、【User-Agent】
        httpPost.setHeader("Content-type", "application/json");
        httpPost.setHeader("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)");
        httpPost.setHeader("Accept", "application/json");
        httpPost.setHeader("Authorization",
                "WECHATPAY2-SHA256-RSA2048 " + post);
        //执行请求操作，并拿到结果（同步阻塞）
        CloseableHttpResponse response = client.execute(httpPost);
        //获取结果实体
        HttpEntity entity = response.getEntity();
        if (entity != null) {
            //按指定编码转换结果实体为String类型
            body = EntityUtils.toString(entity, charset);
            logger.info(body);
        }
        EntityUtils.consume(entity);
        //释放链接
        response.close();
        switch (url) {
            case "v3/pay/transactions/app"://返回APP支付所需的参数
                return JSONObject.fromObject(body).getString("prepay_id");
            case "v3/pay/transactions/jsapi"://返回JSAPI支付所需的参数
                return JSONObject.fromObject(body).getString("prepay_id");
            case "v3/pay/transactions/native"://返回native的请求地址
                return JSONObject.fromObject(body).getString("code_url");
            case "v3/pay/transactions/h5"://返回h5支付的链接
                return JSONObject.fromObject(body).getString("h5_url");
        }
        return null;
    }

    /**
     * 微信调起支付参数
     * 返回参数如有不理解 请访问微信官方文档
     * https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter4_1_4.shtml
     *
     * @param prepayId           微信下单返回的prepay_id
     * @param appId              应用ID(appid)
     * @param privateKeyFilePath 私钥的地址
     * @return 当前调起支付所需的参数
     * @throws Exception
     */
    public static JSONObject WxTuneUp(String prepayId, String appId, String privateKeyFilePath) throws Exception {
        String time = System.currentTimeMillis() / 1000 + "";
        WxPayUtil KeyPairFactory = new WxPayUtil();
        String nonceStr = KeyPairFactory.generateNonceStr();
        String packageStr = "prepay_id=" + prepayId;
        ArrayList<String> list = new ArrayList<>();
        list.add(appId);
        list.add(time);
        list.add(nonceStr);
        list.add(packageStr);
        //加载签名
        String packageSign = sign(buildSignMessage(list).getBytes(), privateKeyFilePath);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("appid", appId);
        jsonObject.put("timeStamp", time);
        jsonObject.put("nonceStr", nonceStr);
        jsonObject.put("package", packageStr);
        jsonObject.put("signType", "RSA");
        jsonObject.put("paySign", packageSign);
        return jsonObject;
    }

    /**
     * 生成组装请求头
     *
     * @param method             请求方式
     * @param url                请求地址
     * @param mercId             商户ID
     * @param serial_no          证书序列号
     * @param privateKeyFilePath 私钥路径
     * @param body               请求体
     * @return 组装请求的数据
     * @throws Exception
     */
    static String getToken(String method, HttpUrl url, String mercId, String serial_no, String privateKeyFilePath, String body) throws Exception {
        WxPayUtil KeyPairFactory = new WxPayUtil();
        String nonceStr = KeyPairFactory.generateNonceStr();
        long timestamp = System.currentTimeMillis() / 1000;
        String message = buildMessage(method, url, timestamp, nonceStr, body);
        String signature = sign(message.getBytes("UTF-8"), privateKeyFilePath);
        return "mchid=\"" + mercId + "\","
                + "nonce_str=\"" + nonceStr + "\","
                + "timestamp=\"" + timestamp + "\","
                + "serial_no=\"" + serial_no + "\","
                + "signature=\"" + signature + "\"";
    }
    /**
     * 生成签名
     *
     * @param message            请求体
     * @param privateKeyFilePath 私钥的路径
     * @return 生成base64位签名信息
     * @throws Exception
     */
    static String sign(byte[] message, String privateKeyFilePath) throws Exception {
        Signature sign = Signature.getInstance("SHA256withRSA");
        sign.initSign(getPrivateKey(privateKeyFilePath));
        sign.update(message);
        return Base64.getEncoder().encodeToString(sign.sign());
    }

    /**
     * 组装签名加载
     *
     * @param method    请求方式
     * @param url       请求地址
     * @param timestamp 请求时间
     * @param nonceStr  请求随机字符串
     * @param body      请求体
     * @return 组装的字符串
     */
    static String buildMessage(String method, HttpUrl url, long timestamp, String nonceStr, String body) {
        String canonicalUrl = url.encodedPath();
        if (url.encodedQuery() != null) {
            canonicalUrl += "?" + url.encodedQuery();
        }
        return method + "\n"
                + canonicalUrl + "\n"
                + timestamp + "\n"
                + nonceStr + "\n"
                + body + "\n";
    }

    /**
     * 获取私钥。
     *
     * @param filename 私钥文件路径  (required)
     * @return 私钥对象
     */
    static PrivateKey getPrivateKey(String filename) throws IOException {
        String content = new String(Files.readAllBytes(Paths.get(filename)), "UTF-8");
        try {
            String privateKey = content.replace("-----BEGIN PRIVATE KEY-----", "")
                    .replace("-----END PRIVATE KEY-----", "")
                    .replaceAll("\\s+", "");
            KeyFactory kf = KeyFactory.getInstance("RSA");
            return kf.generatePrivate(
                    new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey)));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("当前Java环境不支持RSA", e);
        } catch (InvalidKeySpecException e) {
            throw new RuntimeException("无效的密钥格式");
        }
    }

    /**
     * 构造签名串
     *
     * @param signMessage 待签名的参数
     * @return 构造后带待签名串
     */
    static String buildSignMessage(ArrayList<String> signMessage) {
        if (signMessage == null || signMessage.size() <= 0) {
            return null;
        }
        StringBuilder sbf = new StringBuilder();
        for (String str : signMessage) {
            sbf.append(str).append("\n");
        }
        return sbf.toString();
    }


}
